package com.hdu.joinInMemory.factory;


import com.hdu.joinInMemory.executor.JoinItemExecutor;
import com.hdu.joinInMemory.executor.JoinItemExecutorAdapter;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;

import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.reflect.FieldUtils.getAllFieldsList;
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
import static org.springframework.core.annotation.AnnotatedElementUtils.hasAnnotation;


/***
 * 创建基于注解实现的 JoinItemExecutor
 * @param <A>
 */
abstract class AbstractAnnotationBasedJoinItemExecutorFactory<A extends Annotation>
        implements JoinItemExecutorFactory {
    private final Class<A> annotationCls;

    protected AbstractAnnotationBasedJoinItemExecutorFactory(Class<A> annotationCls) {
        this.annotationCls = annotationCls;
    }

    @Override
    public <DATA> List<JoinItemExecutor<DATA>> createForType(Class<DATA> cls) {
        return getAllFieldsList(cls)
                .stream()
                .filter(field -> hasAnnotation(field, annotationCls))
                .map(field -> createByClass(cls, field, findMergedAnnotation(field, annotationCls)))
                .filter(Objects::nonNull)
                .collect(toList());
    }

    private <DATA> JoinItemExecutor<DATA> createByClass(Class<DATA> cls, Field field, A annotation) {
        JoinItemExecutorAdapter adapter = JoinItemExecutorAdapter.builder()
                .name(createName(cls, field, annotation))
                .runLevel(createRunLevel(cls, field, annotation))
                .keyFromSourceData(createKeyGeneratorFromData(cls, field, annotation))
                .joinDataLoader(createDataLoader(cls, field, annotation))
                .keyFromJoinData(createKeyGeneratorFromJoinData(cls, field, annotation))
                .joinDataConverter(createDataConverter(cls, field, annotation))
                .foundCallback(createFoundFunction(cls, field, annotation))
                .lostCallback(createLostFunction(cls, field, annotation))
                .build();
        return adapter;
    }

    protected <DATA> BiConsumer<Object, Object> createLostFunction(Class<DATA> cls, Field field, A ann) {
        return null;
    }

    protected abstract <DATA> BiConsumer<Object, List<Object>> createFoundFunction(Class<DATA> cls, Field field, A ann);

    protected abstract <DATA> Function<Object, Object> createDataConverter(Class<DATA> cls, Field field, A ann);

    protected abstract <DATA> Function<Object, Object> createKeyGeneratorFromJoinData(Class<DATA> cls, Field field, A ann);

    protected abstract <DATA> Function<List<Object>, List<Object>> createDataLoader(Class<DATA> cls, Field field, A ann);

    protected abstract <DATA> Function<Object, Object> createKeyGeneratorFromData(Class<DATA> cls, Field field, A ann);

    protected abstract <DATA> int createRunLevel(Class<DATA> cls, Field field, A ann);

    protected <DATA> String createName(Class<DATA> cls, Field field, A ann) {
        return new StringBuilder()
                .append("class[").append(cls.getSimpleName()).append("]")
                .append("#field[").append(field.getName()).append("]")
                .append("-").append(ann.getClass().getSimpleName())
                .toString();
    }
}
